/*
 * @Author: yuta
 * @Date: 2021-08-27 15:35:54
 * @LastEditTime: 2021-09-07 18:07:12
 * @LastEditors: Drc
 */
/*
 * @Descripttion:
 * @Author: huangjun
 * @Date: 2020-07-30 10:09:14
 * @LastEditors: huangjun
 * @LastEditTime: 2021-01-13 17:27:25
 */
import React, {Component} from 'react'
import {
  View,
  Text,
  TextInput,
  StyleSheet,
  I18nManager,
  StyleProp,
  ViewStyle,
  TextStyle,
  KeyboardTypeOptions,
  TextInputProps,
} from 'react-native'
import * as Animatable from 'react-native-animatable'

const styles = StyleSheet.create({
  containerDefault: {},
  cellDefault: {
    borderColor: '#979797',
    borderWidth: 0.5,
  },
  cellFocusedDefault: {
    borderColor: 'black',
    borderWidth: 2,
  },
  textStyleDefault: {
    color: '#333',
    fontSize: 24,
  },
  textStyleFocusedDefault: {
    color: 'black',
  },
})

interface Props extends TextInputProps {
  value?: string
  codeLength?: number
  cellSize?: number
  cellSpacing?: number
  placeholderText?: string | React.ReactElement
  mask?: string | React.ReactElement
  maskDelay?: number
  password?: boolean
  autoFocus?: boolean
  restrictToNumbers?: boolean
  containerStyle?: StyleProp<ViewStyle>
  cellStyle?: StyleProp<ViewStyle>
  cellStyleFocused?: StyleProp<ViewStyle>
  cellStyleFilled?: StyleProp<ViewStyle>
  textStyle?: StyleProp<TextStyle>
  textStyleFocused?: StyleProp<TextStyle>
  animated?: boolean
  animationFocused?: any
  onFulfill?: (code: any) => void
  onChangeText?: (code: string) => void
  onBackspace?: () => void
  onTextChange?: (code: string) => void
  testID?: any
  onFocus?: () => void
  onBlur?: () => void
  keyboardType?: KeyboardTypeOptions
  editable?: boolean
  disableFullscreenUI?: boolean
}

class PinCodeInput extends Component<Props> {
  state = {
    maskDelay: false,
    focused: false,
  }
  ref = React.createRef<View>()
  inputRef = React.createRef<TextInput>()
  maskTimeout = null
  animate = ({animation = 'shake', duration = 650}) => {
    if (!this.props.animated) {
      return new Promise((resolve, reject) => reject(new Error('Animations are disabled')))
    }
    return this.ref.current[animation](duration)
  }

  shake = () => this.animate({animation: 'shake'})

  focus = () => {
    return this.inputRef.current.focus()
  }

  blur = () => {
    return this.inputRef.current.blur()
  }

  clear = () => {
    return this.inputRef.current.clear()
  }

  _inputCode = (code) => {
    const {password, codeLength = 4, onTextChange, onFulfill} = this.props

    if (this.props.restrictToNumbers) {
      code = (code.match(/[0-9]/g) || []).join('')
    }
    if (onTextChange) {
      onTextChange(code)
    }

    if (code.length === codeLength && onFulfill) {
      onFulfill(code)
    }

    // handle password mask
    const maskDelay = password && code.length > this.props.value.length // only when input new char
    this.setState({maskDelay})

    if (maskDelay) {
      // mask password after delay
      clearTimeout(this.maskTimeout)
      this.maskTimeout = setTimeout(() => {
        this.setState({maskDelay: false})
      }, this.props.maskDelay)
    }
  }

  _keyPress = (event) => {
    if (event.nativeEvent.key === 'Backspace') {
      const {value, onBackspace} = this.props
      if (value === '' && onBackspace) {
        onBackspace()
      }
    }
  }

  _onFocused = () => {
    this.setState({focused: true})
    if (typeof this.props.onFocus === 'function') {
      this.props.onFocus()
    }
  }

  _onBlurred = () => {
    this.setState({focused: false})
    if (typeof this.props.onBlur === 'function') {
      this.props.onBlur()
    }
  }

  componentWillUnmount() {
    clearTimeout(this.maskTimeout)
  }

  render() {
    const {
      value,
      codeLength,
      cellSize,
      cellSpacing,
      placeholderText,
      password,
      mask,
      autoFocus,
      containerStyle,
      cellStyle,
      cellStyleFocused,
      cellStyleFilled,
      textStyle,
      keyboardType,
      animationFocused,
      animated,
      testID,
      editable,
      disableFullscreenUI,
      ...others
    } = this.props
    const {maskDelay, focused} = this.state
    return (
      <Animatable.View
        ref={this.ref}
        style={[
          {
            alignItems: 'stretch',
            flexDirection: 'row',
            justifyContent: 'center',
            position: 'relative',
            width: cellSize * codeLength + cellSpacing * (codeLength - 1),
            height: cellSize,
          },
          containerStyle,
        ]}>
        <View
          style={{
            position: 'absolute',
            margin: 0,
            height: '100%',
            flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
            alignItems: 'center',
          }}>
          {Array.apply(null, Array(codeLength)).map((_, idx) => {
            const cellFocused = focused && idx === value.length
            const filled = idx < value.length
            const last = idx === value.length - 1
            const showMask = filled && password && (!maskDelay || !last)
            const isPlaceholderText = typeof placeholderText === 'string'
            const isMaskText = typeof mask === 'string'
            const pinCodeChar = value.charAt(idx)

            let cellText = null
            if (filled || placeholderText !== null) {
              if (showMask && isMaskText) {
                cellText = mask
              } else if (!filled && isPlaceholderText) {
                cellText = placeholderText
              } else if (pinCodeChar) {
                cellText = pinCodeChar
              }
            }

            const placeholderComponent = !isPlaceholderText ? placeholderText : null
            const maskComponent = showMask && !isMaskText ? mask : null
            const isCellText = typeof cellText === 'string'

            return (
              <Animatable.View
                key={idx}
                style={[
                  {
                    width: cellSize,
                    height: cellSize,
                    marginLeft: cellSpacing / 2,
                    marginRight: cellSpacing / 2,
                    flexDirection: 'row',
                    alignItems: 'center',
                    justifyContent: 'center',
                  },
                  cellStyle,
                  cellSpacing === 0 &&
                    idx < codeLength - 1 && {
                      borderRightColor: 'transparent',
                      borderRightWidth: null,
                    },
                  cellFocused ? cellStyleFocused : {},
                  filled ? cellStyleFilled : {},
                ]}
                animation={idx === value.length && focused && animated ? animationFocused : null}
                iterationCount="infinite"
                duration={500}>
                {isCellText && !maskComponent && <Text style={textStyle}>{cellText}</Text>}

                {!isCellText && !maskComponent && placeholderComponent}
                {isCellText && maskComponent}
              </Animatable.View>
            )
          })}
        </View>
        <TextInput
          disableFullscreenUI={disableFullscreenUI}
          value={value}
          ref={this.inputRef}
          onChangeText={this._inputCode}
          onKeyPress={this._keyPress}
          onFocus={() => this._onFocused()}
          onBlur={() => this._onBlurred()}
          spellCheck={false}
          autoFocus={autoFocus}
          keyboardType={keyboardType}
          numberOfLines={1}
          caretHidden
          maxLength={codeLength}
          selection={{
            start: value.length,
            end: value.length,
          }}
          style={{
            flex: 1,
            opacity: 0,
            textAlign: 'center',
          }}
          testID={testID || undefined}
          editable={editable}
          {...others}
        />
      </Animatable.View>
    )
  }

  static defaultProps = {
    value: '',
    codeLength: 4,
    cellSize: 48,
    cellSpacing: 4,
    placeholderText: '',
    password: false,
    mask: '*',
    maskDelay: 200,
    keyboardType: 'numeric',
    autoFocus: false,
    restrictToNumbers: false,
    containerStyle: styles.containerDefault,
    cellStyle: styles.cellDefault,
    cellStyleFocused: styles.cellFocusedDefault,
    textStyle: styles.textStyleDefault,
    textStyleFocused: styles.textStyleFocusedDefault,
    animationFocused: 'pulse',
    animated: true,
    editable: true,
    inputProps: {},
    disableFullscreenUI: true,
  }
}

export default PinCodeInput
